{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "bd2b2eba-b7fd-4856-960f-f2cbadcc12af",
   "metadata": {},
   "source": [
    "# Building a Metaphor Data Agent\n",
    "\n",
    "This tutorial walks through using the LLM tools provided by the [Metaphor API](https://platform.metaphor.systems/) to allow LLMs to easily search and retrieve HTML content from the Internet.\n",
    "\n",
    "To get started, you will need an [OpenAI api key](https://platform.openai.com/account/api-keys) and a [Metaphor API key](https://dashboard.metaphor.systems/overview)\n",
    "\n",
    "We will import the relevant agents and tools and pass them our keys here:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "df2a0ecd-22e9-4cef-b069-89e4286e4d75",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "search\n",
      "retrieve_documents\n",
      "search_and_retrieve_documents\n",
      "find_similar\n",
      "current_date\n"
     ]
    }
   ],
   "source": [
    "# Set up OpenAI\n",
    "import os\n",
    "\n",
    "os.environ[\"OPENAI_API_KEY\"] = \"sk-your-key\"\n",
    "\n",
    "from llama_index.core.agent.workflow import FunctionAgent\n",
    "from llama_index.llms.openai import OpenAI\n",
    "\n",
    "# Set up Metaphor tool\n",
    "from llama_index.tools.metaphor.base import MetaphorToolSpec\n",
    "\n",
    "metaphor_tool = MetaphorToolSpec(\n",
    "    api_key=\"your-key\",\n",
    ")\n",
    "\n",
    "metaphor_tool_list = metaphor_tool.to_tool_list()\n",
    "for tool in metaphor_tool_list:\n",
    "    print(tool.metadata.name)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fe8e3012-bab0-4e55-858a-e3721282552c",
   "metadata": {},
   "source": [
    "## Testing the Metaphor tools\n",
    "\n",
    "We've imported our OpenAI agent, set up the api key, and initialized our tool, checking the methods that it has available. Let's test out the tool before setting up our Agent.\n",
    "\n",
    "All of the Metaphor search tools make use of the `AutoPrompt` option where Metaphor will pass the query through an LLM to refine and improve it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e64da618-b4ab-42d7-903d-f4eeb624f43c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[Metaphor Tool] Autoprompt: Here's a great resource for learning about machine learning transformers:\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[{'title': 'Natural Language Processing with Transformers Book',\n",
       "  'url': 'https://transformersbook.com/',\n",
       "  'id': 'm0fnocgD1zPLxtDZuGt5JQ'},\n",
       " {'title': 'Transformers',\n",
       "  'url': 'https://www.nlpdemystified.org/course/transformers',\n",
       "  'id': 'jPHVA37dax24EwEan9jj0g'},\n",
       " {'title': 'transformersbook (Natural Language Processing with Transformers)',\n",
       "  'url': 'https://huggingface.co/transformersbook',\n",
       "  'id': 'lcsZRBBHevoB4wAn1SFMtA'}]"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "metaphor_tool.search(\"machine learning transformers\", num_results=3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8ff3e8c3-3d43-4915-9b35-89d1d944639e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[Document(id_='4ab1448d-663a-4dfd-8dc0-dd7ba860e42d', embedding=None, metadata={}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, hash='bbd2ad7f78cf02100add6077decdf885ffebfe37743bfc13e80e61b5b833e46c', text='<div><div><p>\\n <a href=\"/static/transformers-bumblebee-d4ec62a6a46aae8f83b7419242f59f4e-48854.jpg\">\\n \\n \\n \\n \\n \\n \\n \\n </a>\\n </p>\\n<p>In this post, we will be describing a class of sequence processing models known as Transformers (…<em>robots in disguise</em>).\\nJokes aside, Transformers came out on the scene not too long ago and have rocked the natural language processing community because of\\ntheir pitch: state-of-the-art and efficient sequence processing <strong>without recurrent units or convolution</strong>. </p>\\n<p><em>“No recurrent units or convolution?! What are these models even made of?!”</em>, you may be exclaiming to unsuspecting strangers on the streets. </p>\\n<p>Not much it turns out, other than a bunch of attention and feedforward operations. </p>\\n<p>While the individual components that make up the Transformer model are not particularly novel, this is still a pretty dense paper with a lot\\nof moving parts. So our aim in this post will be to distill the model to its key contributions, without getting too stuck\\nin the details.</p>\\n<p>But first, the TLDR for the paper:</p>\\n<ol>\\n<li><strong>Transformers demonstrate that recurrence and convolution are not essential for building high-performance natural language models</strong></li>\\n<li><strong>They achieve state-of-the-art machine translation results using a self-attention operation</strong></li>\\n<li><strong>Attention is a highly-efficient operation due to its parallelizability and runtime characteristics</strong></li>\\n</ol>\\n<p>If that sounds exciting, read onward!</p>\\n<h2>How Transformers Work</h2>\\n<p>While the Transformer does not use traditional recurrent units or convolutions, it still takes inspiration from\\nsequence-to-sequence architectures where we encode some input and iteratively decode a desired output. </p>\\n<p>How does this play out in practice? Let’s focus on the encoder first. There are quite a few elements to the process,\\nso don’t get too lost in the details. All we are doing is encoding some inputs 🙂.</p>\\n<p>Assume we start with a certain phrase that we would like to translate from Spanish to English. The Transformer\\nbegins by embedding the tokens of the Spanish phrase into a conventional embedding matrix:</p>\\n<p>\\n <a href=\"/static/input_embedding_matrix-a2e9df88621a1dae9c4c16b097b808ae-1c473.png\">\\n \\n \\n \\n \\n \\n \\n \\n </a>\\n </p>\\n<p>Because the model makes no use of recurrence, we need some way to represent position-based information\\nin the model. Hence we add a positional encoding to this embedding matrix, whose exact form we will describe\\nin the next section:</p>\\n<p>\\n <a href=\"/static/positional_encoding_added-9eb9de50b7ddfe4287ef9e386649e292-d8e49.png\">\\n \\n \\n \\n \\n \\n \\n \\n </a>\\n </p>\\n<p>Our modified input is fed into the first layer of the Transformer encoder. Within each encoder layer,\\nwe perform a series of operations on the inputs.</p>\\n<p>First off, we feed the input through a multi-head attention operation:</p>\\n<p>\\n <a href=\"/static/multihead_attention_encoder-503fd3d322872609873e62cbb1ca98d4-71c2a.png\">\\n \\n \\n \\n \\n \\n \\n \\n </a>\\n </p>\\n<p>To this attention output, we also add a residual connection as well as perform a layer normalization step:</p>\\n<p>\\n <a href=\"/static/residual_layer_to_multihead_attention-8249f2237caed6af900dabf68ef86f39-14', start_char_idx=None, end_char_idx=None, text_template='{metadata_str}\\n\\n{content}', metadata_template='{key}: {value}', metadata_seperator='\\n')]"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "metaphor_tool.retrieve_documents([\"iEYMai5rS9k0hN5_BH0VZg\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c55a0c7b-4c58-4725-8543-29bb1b7278ed",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'title': 'Transformers: Attention in Disguise',\n",
       "  'url': 'https://www.mihaileric.com/posts/transformers-attention-in-disguise/',\n",
       "  'id': 'iEYMai5rS9k0hN5_BH0VZg'},\n",
       " {'title': 'Transformers: a Primer',\n",
       "  'url': 'http://www.columbia.edu/~jsl2239/transformers.html',\n",
       "  'id': 'cXbT9IsR5u8NtLTAIcUWOA'},\n",
       " {'title': 'Illustrated Guide to Transformers- Step by Step Explanation',\n",
       "  'url': 'https://towardsdatascience.com/illustrated-guide-to-transformers-step-by-step-explanation-f74876522bc0?gi=8fe76db5c4d9',\n",
       "  'id': 'czTCPZ1vqo-f92WQwKvRig'}]"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "metaphor_tool.find_similar(\n",
    "    \"https://www.mihaileric.com/posts/transformers-attention-in-disguise/\"\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1fc8665d-ddb8-411f-b187-93a132d19e7a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[Document(id_='d6408474-33d9-451b-9742-0634b8aea667', embedding=None, metadata={}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, hash='37e68a115716214b23b68f40a70cca20943f3161fc6b9a1bed5ab21347d0a41e', text='Dale’s Blog → https://goo.gle/3xOeWoK\\nClassify text with BERT → https://goo.gle/3AUB431\\n\\nOver the past five years, Transformers, a neural network architecture, have completely transformed state-of-the-art natural language processing. Want to translate text with machine learning? Curious how an ML model could write a poem or an op ed? Transformers can do it all. In this episode of Making with ML, Dale Markowitz explains what transformers are, how they work, and why they’re so impactful. Watch to learn how you can start using transformers in your app!\\n\\nChapters: \\n0:00 - Intro\\n0:51 - What are transformers?\\n3:18 - How do transformers work?\\n7:41 - How are transformers used?\\n8:35 - Getting started with transformers\\n\\nWatch more episodes of Making with Machine Learning → https://goo.gle/2YysJRY \\n\\nSubscribe to Google Cloud Tech → https://goo.gle/GoogleCloudTech \\n\\n#MakingwithMachineLearning #MakingwithML\\n\\nproduct: Cloud - General; fullname: Dale Markowitz; re_ty: Publish;\\n| view_count: 259,903 views | short_view_count: 259K views | num_likes: 9.6K | num_subscribers: 942doftext|><|endoftext|>', start_char_idx=None, end_char_idx=None, text_template='{metadata_str}\\n\\n{content}', metadata_template='{key}: {value}', metadata_seperator='\\n')]"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "metaphor_tool.search_and_retrieve_documents(\n",
    "    \"This is the best explanation for machine learning transformers:\", num_results=1\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1210906d-87a7-466a-9712-1d17dba2c2ec",
   "metadata": {},
   "source": [
    "We can see we have different tools to search for results, retrieve the results, find similar results to a web page, and finally a tool that combines search and document retrieval into a single tool. We will test them out in LLM Agents below:\n",
    "\n",
    "### Using the Search and Retrieve documents tools in an Agent\n",
    "\n",
    "We can create an agent with access to the above tools and start testing it out:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9d88c2ee-184a-4371-995b-a086b34db24f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# We don't give the Agent our unwrapped retrieve document tools, instead passing the wrapped tools\n",
    "agent = FunctionAgent(\n",
    "    tools=metaphor_tool_list,\n",
    "    llm=OpenAI(model=\"gpt-4.1\"),\n",
    ")\n",
    "\n",
    "# Context to store chat history\n",
    "ctx = Context(agent)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f69a53fd-55c4-4e18-8fbe-6a29d5f3cef0",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(await agent.run(\"What are the best resturants in toronto?\", ctx=ctx))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f44035e9-27ab-47b7-abc5-cf2fe5d1482f",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(await agent.run(\"tell me more about Osteria Giulia\", ctx=ctx))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "939c7b98-0d75-4ef0-ac47-fd3bd24d3e50",
   "metadata": {},
   "source": [
    "## Avoiding Context Window Issues\n",
    "\n",
    "The above example shows the core uses of the Metaphor tool. We can easily retrieve a clean list of links related to a query, and then we can fetch the content of the article as a cleaned up html extract. Alternatively, the search_and_retrieve_documents tool directly returns the documents from our search result.\n",
    "\n",
    "We can see that the content of the articles is somewhat long compared to current LLM context windows, and so to allow retrieval and summary of many documents we will set up and use another tool from LlamaIndex that allows us to load text into a VectorStore, and query it for retrieval. This is where the `search_and_retrieve_documents` tool become particularly useful. The Agent can make a single query to retrieve a large number of documents, using a very small number of tokens, and then make queries to retrieve specific information from the documents."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a017cc61-1696-4a03-8d09-a628f9049cfd",
   "metadata": {},
   "outputs": [],
   "source": [
    "from llama_index.tools.tool_spec.load_and_search.base import LoadAndSearchToolSpec\n",
    "\n",
    "# The search_and_retrieve_documents tool is the third in the tool list, as seen above\n",
    "wrapped_retrieve = LoadAndSearchToolSpec.from_defaults(\n",
    "    metaphor_tool_list[2],\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "80b47437-8f6d-4e94-97ca-4e35f78336f2",
   "metadata": {},
   "source": [
    "Our wrapped retrieval tools separate loading and reading into separate interfaces. We use `load` to load the documents into the vector store, and `read` to query the vector store. Let's try it out again"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e4f81bd3-a5b9-452c-93f4-91d16c4c0df1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[Metaphor Tool] Autoprompt: Here is a great article explaining machine learning transformers:\n",
      "\n",
      "A transformer is a type of artificial neural network used in natural language processing (NLP) tasks such as machine translation, text summarization, and question answering. It is based on the Transformer architecture, which was introduced in 2017 by Google researchers. The Transformer architecture uses attention mechanisms to learn the relationships between words in a sentence, allowing it to better understand the context of the sentence and generate more accurate translations.\n",
      "\n",
      "The first paper on transformers was written by Ashish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, Llion Jones, Aidan N. Gomez, Lukasz Kaiser, and Illia Polosukhin in 2017. The paper was titled \"Attention Is All You Need\".\n"
     ]
    }
   ],
   "source": [
    "wrapped_retrieve.load(\"This is the best explanation for machine learning transformers:\")\n",
    "print(wrapped_retrieve.read(\"what is a transformer\"))\n",
    "print(wrapped_retrieve.read(\"who wrote the first paper on transformers\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "85be6977-c4e8-43a4-99be-3322d4b72b07",
   "metadata": {},
   "source": [
    "## Creating the Agent\n",
    "\n",
    "We now are ready to create an Agent that can use Metaphors services to it's full potential. We will use our wrapped read and load tools, as well as the `get_date` utility for the following agent and test it out below:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3a893f26-dbb6-4b72-9795-702eaf749564",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Just pass the wrapped tools and the get_date utility\n",
    "agent = FunctionAgent(\n",
    "    tools=[*wrapped_retrieve.to_tool_list(), metaphor_tool_list[4]],\n",
    "    llm=OpenAI(model=\"gpt-4.1\"),\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5835d058-da9c-4d42-9d2a-941c73b88a51",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\n",
    "    await agent.run(\n",
    "        \"Can you summarize everything published in the last month regarding news on\"\n",
    "        \" superconductors\"\n",
    "    )\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a7ee91ca-6730-4fdd-8189-ac21022f34f1",
   "metadata": {},
   "source": [
    "We asked the agent to retrieve documents related to superconductors from this month. It used the `get_date` tool to determine the current month, and then applied the filters in Metaphor based on publication date when calling `search`. It then loaded the documents using `retrieve_documents` and read them using `read_retrieve_documents`.\n",
    "\n",
    "We can make another query to the vector store to read from it again, now that the articles are loaded:"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "llama_hub",
   "language": "python",
   "name": "llama_hub"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
